package com.gdust.hzx.order.service.impl;

import com.gdust.hzx.feign.cart.CartFeignClient;
import com.gdust.hzx.feign.product.ProductFeignClient;
import com.gdust.hzx.feign.user.UserFeignClient;
import com.gdust.hzx.model.dto.h5.OrderInfoDto;
import com.gdust.hzx.model.entity.h5.CartInfo;
import com.gdust.hzx.model.entity.order.OrderInfo;
import com.gdust.hzx.model.entity.order.OrderItem;
import com.gdust.hzx.model.entity.order.OrderLog;
import com.gdust.hzx.model.entity.product.ProductSku;
import com.gdust.hzx.model.entity.user.UserAddress;
import com.gdust.hzx.model.entity.user.UserInfo;
import com.gdust.hzx.model.vo.common.ResultCodeEnum;
import com.gdust.hzx.model.vo.h5.TradeVo;
import com.gdust.hzx.order.mapper.OrderInfoMapper;
import com.gdust.hzx.order.mapper.OrderItemMapper;
import com.gdust.hzx.order.mapper.OrderLogMapper;
import com.gdust.hzx.order.service.OrderInfoService;
import com.gdust.hzx.utils.AuthContextUtil;
import com.gdust.spzx.common.exception.GdustException;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * ClassName: OrderInfoServiceImpl
 * Package: com.gdust.hzx.order.service.impl
 * Description: 订单信息服务实现类，提供订单相关的业务逻辑
 * <p>
 * 该类实现了 `OrderInfoService` 接口，主要负责处理与订单相关的业务逻辑，包括获取订单明细、计算订单总金额等。
 *
 * @Author syun
 * @Create 2024/12/27 21:31
 * @Version 1.0
 */
@Service// 注解表明该类是服务层的一个组件，Spring 会自动扫描并注入该类
public class OrderInfoServiceImpl implements OrderInfoService {
    /**
     * 注入远程调用接口，用于与服务端的购物车模块进行交互。
     * 通过 CartFeignClient 实现与购物车服务的交互，获取购物车中选中的商品列表。
     */
    @Autowired
    private CartFeignClient cartFeignClient;

    /**
     * 注入远程调用接口，用于与服务端的产品模块进行交互。
     */
    @Autowired
    private ProductFeignClient productFeignClient;

    /**
     * 注入远程调用接口，用于与服务端的用户模块进行交互。
     */
    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * // 注入 OrderInfoMapper，用于操作订单信息数据表
     */
    @Autowired
    private OrderInfoMapper orderInfoMapper;

    /**
     * // 注入 OrderItemMapper，用于操作订单项数据表
     */
    @Autowired
    private OrderItemMapper orderItemMapper;

    /**
     * // 注入 OrderLogMapper，用于操作订单日志数据表
     */
    @Autowired
    private OrderLogMapper orderLogMapper;


    /**
     * 确认下单接口
     * <p>
     * 该方法用于处理确认下单的逻辑，远程调用购物车模块获取选中的商品，转换为订单明细，计算订单总金额，并封装为 `TradeVo` 返回。
     *
     * @return TradeVo 订单交易信息，包括订单项列表和支付总金额
     */
    @Override
    public TradeVo getTrade() {
        // 1. 远程调用获取购物车选中商品列表（调用购物车模块的 getAllChecked 方法）
        List<CartInfo> cartInfoList = cartFeignClient.getAllChecked();

        // 2. 创建一个 List 集合用于封装订单项
        List<OrderItem> orderItemList = new ArrayList<>();
        // 将购物车中的每个商品转换成订单项
        for (CartInfo cartInfo : cartInfoList) {
            OrderItem orderItem = new OrderItem();
            orderItem.setSkuId(cartInfo.getSkuId());// 设置商品SKU ID
            orderItem.setSkuName(cartInfo.getSkuName()); // 设置商品名称
            orderItem.setSkuNum(cartInfo.getSkuNum()); // 设置商品数量
            orderItem.setSkuPrice(cartInfo.getCartPrice()); // 设置商品价格
            orderItem.setThumbImg(cartInfo.getImgUrl()); // 设置商品缩略图
            orderItemList.add(orderItem);// 将订单项添加到列表
        }


        // 3. 计算订单支付总金额
        BigDecimal totalAmount = new BigDecimal(0);// 初始化总金额为0
        for (OrderItem orderItem : orderItemList) {
            // 计算每个商品的总价格，并累加到 totalAmount
            totalAmount = totalAmount
                    .add(orderItem.getSkuPrice()
                            .multiply(new BigDecimal(orderItem.getSkuNum())));
        }

        // 4. 封装订单交易信息（包括订单项列表和支付总金额）
        TradeVo tradeVo = new TradeVo();
        tradeVo.setOrderItemList(orderItemList);// 设置订单项列表
        tradeVo.setTotalAmount(totalAmount);// 设置支付总金额

        // 5.返回订单交易信息
        return tradeVo;
    }

    /**
     * 订单信息条件
     *
     * @param orderInfoDto 订单信息条件
     * @return
     */
    //生成订单
    @Transactional
    @Override
    public Long submitOrder(OrderInfoDto orderInfoDto) {
        //1 orderInfoDto获取所有订单项list List<OrderItem>
        List<OrderItem> orderItemList = orderInfoDto.getOrderItemList();

        //2 判断List<OrderItem>为空，抛出异常
        if (CollectionUtils.isEmpty(orderItemList)) {
            throw new GdustException(ResultCodeEnum.DATA_ERROR);
        }

        //3 校验商品库存是否充足
        // 遍历List<OrderItem>集合，得到每个orderItem
        for (OrderItem orderItem : orderItemList) {
            //根据skuId获取sku信息，远程调用获取商品sku信息，（包含库存量）
            ProductSku productSku = productFeignClient.getBySkuId(orderItem.getSkuId());
            if (productSku == null) {
                throw new GdustException(ResultCodeEnum.DATA_ERROR);
            }
            // 校验每个orderItem库存量是否充足
            if (orderItem.getSkuNum().intValue() > productSku.getStockNum().intValue()) {
                throw new GdustException(ResultCodeEnum.STOCK_LESS);
            }
        }

        //4 添加数据到order_info表
        // 封装数据到OrderInfo对象
        OrderInfo orderInfo = new OrderInfo();
        UserInfo userInfo = AuthContextUtil.getUserInfo();
        //订单编号
        orderInfo.setOrderNo(String.valueOf(System.currentTimeMillis()));
        //用户id
        orderInfo.setUserId(userInfo.getId());
        //用户昵称
        orderInfo.setNickName(userInfo.getNickName());

        //封装收货地址信息
        Long userAddressId = orderInfoDto.getUserAddressId();
        //远程调用：根据收货地址id，获取用户收货地址信息
        UserAddress userAddress = userFeignClient.getUserAddress(userAddressId);
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getFullAddress());
        //订单金额
        BigDecimal totalAmount = new BigDecimal(0);
        for (OrderItem orderItem : orderItemList) {
            totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
        }
        orderInfo.setTotalAmount(totalAmount);
        orderInfo.setCouponAmount(new BigDecimal(0));
        orderInfo.setOriginalTotalAmount(totalAmount);
        orderInfo.setFeightFee(orderInfoDto.getFeightFee());
        orderInfo.setPayType(2);
        orderInfo.setOrderStatus(0);

        orderInfoMapper.save(orderInfo);

        //5 添加数据到order_item表
        //添加List<OrderItem>里面数据，把集合每个orderItem添加表
        for (OrderItem orderItem : orderItemList) {
            //设置对应订单id
            orderItem.setOrderId(orderInfo.getId());
            orderItemMapper.save(orderItem);
        }

        //6 添加数据到order_log表
        OrderLog orderLog = new OrderLog();
        orderLog.setOrderId(orderInfo.getId());
        orderLog.setProcessStatus(0);
        orderLog.setNote("提交订单");
        orderLogMapper.save(orderLog);

        //7 远程调用：把生成订单商品，从购物车删除
        cartFeignClient.deleteChecked();

        //8 返回订单id
        return orderInfo.getId();
    }

    /**
     * 获取订单信息
     *
     * @param orderId 订单id
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //自定义方法：获取订单信息
        OrderInfo orderInfoById = orderInfoMapper.getById(orderId);
        return orderInfoById;
    }

    /**
     * 订单模块之立刻购买接口实现
     *
     * @param skuId 商品的 SKU ID，用于识别和获取具体的商品信息
     * @return
     */
    @Override
    public TradeVo buy(Long skuId) {
        // 1. 创建订单项集合，用于存储用户购买的商品信息
        List<OrderItem> orderItemList = new ArrayList<>();

        // 2. 通过远程调用获取商品的详细信息（商品的 SKU 信息），通过 Feign 客户端接口调用
        ProductSku productSku = productFeignClient.getBySkuId(skuId); // 调用产品微服务，获取指定 SKU 的商品信息

        // 3. 创建订单项对象，并为订单项设置商品信息
        OrderItem orderItem = new OrderItem();
        orderItem.setSkuId(skuId); // 设置商品的 SKU ID
        orderItem.setSkuName(productSku.getSkuName()); // 设置商品名称
        orderItem.setSkuNum(1); // 设置购买的商品数量（此处固定为 1）
        orderItem.setSkuPrice(productSku.getSalePrice()); // 设置商品单价（获取商品的销售价格）
        orderItem.setThumbImg(productSku.getThumbImg()); // 设置商品缩略图（商品的图片）

        // 4. 将当前订单项添加到订单项集合中
        orderItemList.add(orderItem);

        // 5. 创建并封装交易信息对象
        TradeVo tradeVo = new TradeVo();
        tradeVo.setOrderItemList(orderItemList); // 设置订单项集合
        tradeVo.setTotalAmount(productSku.getSalePrice()); // 设置总金额（此处总金额即为商品的销售价格）

        // 6. 返回交易信息对象，包含订单项和总金额
        return tradeVo;
    }

    /**
     * 获取订单分页列表
     *
     * @param page        当前页（用于分页查询）
     * @param limit       每页记录数（用于分页查询）
     * @param orderStatus 订单状态，用于筛选不同状态的订单（可为null或具体状态值）
     * @return 返回一个包含订单信息分页的 PageInfo 对象
     */
    @Override
    public PageInfo<OrderInfo> findOrderPage(Integer page, Integer limit, Integer orderStatus) {
        // 1. 使用 PageHelper 进行分页处理，传入当前页码和每页显示的记录数
        //    PageHelper 会拦截当前的 SQL 查询，并为其添加分页功能
        PageHelper.startPage(page, limit);

        // 2. 获取当前登录用户的 ID
        //    从上下文中获取当前用户信息，调用 AuthContextUtil 获取当前登录用户的 ID
        Long userId = AuthContextUtil.getUserInfo().getId();

        // 3. 根据用户 ID 和订单状态查询订单信息列表
        //    调用 orderInfoMapper 的 findUserPage 方法，查询用户的订单信息，支持通过订单状态筛选
        List<OrderInfo> orderInfoList = orderInfoMapper.findUserPage(userId, orderStatus);

        // 4. 遍历订单列表，为每个订单查询并设置其订单项列表
        //    每个订单包含多个订单项，需查询并封装到 OrderInfo 对象的 orderItemList 属性中
        orderInfoList.forEach(orderInfo -> {
            // 4.1 查询该订单的所有订单项（OrderItem）
            //    通过 orderItemMapper 查询当前订单 ID 对应的所有订单项
            List<OrderItem> orderItemList = orderItemMapper.findByOrderId(orderInfo.getId());

            // 4.2 将查询到的订单项设置到当前订单对象的 orderItemList 属性中
            //    每个 OrderInfo 对象包含了该订单的所有订单项数据
            orderInfo.setOrderItemList(orderItemList);
        });

        // 5. 将查询到的订单列表（orderInfoList）封装到 PageInfo 对象中，并返回
        //    PageInfo 是 MyBatis 分页插件提供的分页结果类，包含了分页数据和分页信息（如总页数、当前页码等）
        return new PageInfo<>(orderInfoList);
    }

    /**
     * 根据订单编号获取订单信息（远程调用）
     * @param orderNo 订单号，用于唯一标识订单
     * @return
     */
    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.getOrderInfoByOrderNo(orderNo);
        List<OrderItem> orderItemList = orderItemMapper.findByOrderId(orderInfo.getId());
        orderInfo.setOrderItemList(orderItemList);
        return orderInfo;
    }

    //更新订单状态
    @Override
    public void updateOrderStatus(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.getOrderInfoByOrderNo(orderNo);

        orderInfo.setOrderStatus(1);
        orderInfo.setPaymentTime(new Date());
        orderInfo.setPayType(2);

        orderInfoMapper.updateById(orderInfo);
    }

}
